﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.SqlClient;

namespace Heroius.DBTransitor
{
    /// <summary>
    /// 提供SQL类型与.Net类型之间，及两者实例之间转换相关的功能。
    /// </summary>
    public class SqlDataConverter
    {
        static SqlDataConverter()
        {
            ValueGenerator = new Random();
        }

        /// <summary>
        /// 获取兼容于给定SQL数据类型的.NET类型，若没有兼容类型则返回null。
        /// </summary>
        /// <param name="SqlType">给定的SQL数据类型，由SqlDbType枚举表示</param>
        /// <returns>相应的.NET类型</returns>
        public static Type GetRuntimeType(SqlDbType SqlType)
        {
#if USEDICT
            if (TcSql2Rt.ContainsKey(SqlType))
                return TcSql2Rt[SqlType];
            else return null;
#else
            switch (SqlType)
            {
                case SqlDbType.BigInt: return typeof(Int64);
                case SqlDbType.Binary: return typeof(byte[]);
                case SqlDbType.Bit: return typeof(bool);
                case SqlDbType.Char: return typeof(string);
                //case SqlDbType.Date:return typeof(DateTime);//?
                case SqlDbType.DateTime: return typeof(DateTime);
                //case SqlDbType.DateTime2:return typeof(DateTime);//?
                //case SqlDbType.DateTimeOffset:return typeof(DateTimeOffset);//?
                case SqlDbType.Decimal: return typeof(decimal);
                case SqlDbType.Float: return typeof(double);
                case SqlDbType.Image: return typeof(byte[]);
                case SqlDbType.Int: return typeof(Int32);
                case SqlDbType.Money: return typeof(decimal);
                case SqlDbType.NChar: return typeof(string);
                case SqlDbType.NText: return typeof(string);
                case SqlDbType.NVarChar: return typeof(string);
                case SqlDbType.Real: return typeof(Single);
                case SqlDbType.SmallDateTime: return typeof(DateTime);
                case SqlDbType.SmallInt: return typeof(Int16);
                case SqlDbType.SmallMoney: return typeof(decimal);
                //case SqlDbType.Structured:return typeof(DataTable);//?
                case SqlDbType.Text: return typeof(string);
                //case SqlDbType.Time:return typeof(DateTime);//?
                case SqlDbType.Timestamp: return typeof(byte[]);
                case SqlDbType.TinyInt: return typeof(byte);
                //case SqlDbType.Udt:return typeof(object);//?
                case SqlDbType.UniqueIdentifier: return typeof(Guid);
                case SqlDbType.VarBinary: return typeof(byte[]);
                case SqlDbType.VarChar: return typeof(string);
                case SqlDbType.Variant: return typeof(object);
                //case SqlDbType.Xml:return typeof(System.Data.SqlTypes.SqlXml);//?
                default: return null;
            }
#endif
        }

        /// <summary>
        /// 用于值生成的随即数工具
        /// </summary>
        static Random ValueGenerator;

        /// <summary>
        /// 获取与指定SQL数据类型兼容.NET类型的默认值（对于值类型）或默认新实例（对于引用类型）。
        /// </summary>
        /// <param name="SqlType">给定的SQL数据类型，由SqlDbType枚举表示</param>
        /// <returns>相应.NET类型的默认值</returns>
        public static object GetDefaultValue(SqlDbType SqlType)
        {
            /*如下注释行末尾标记说明了注释代码的原因：
             *  标记 ! 的：对应类型为引用类型，应返回新实例而非default关键字结果（均为null）；
             *  标记 ? 的：对应SQL类型尚未找到合适的.Net类型。
             */
            switch (SqlType)
            {
                case SqlDbType.BigInt: return default(Int64);
                //case SqlDbType.Binary: return default(byte[]);//!
                case SqlDbType.Binary: { byte[] c = new byte[1]; ValueGenerator.NextBytes(c); return c; }
                case SqlDbType.Bit: return default(bool);
                //case SqlDbType.Char: return default(string);//!
                case SqlDbType.Char: return string.Empty;
                //case SqlDbType.Date:return default(DateTime);//?
                //case SqlDbType.DateTime: return default(DateTime);//!
                case SqlDbType.DateTime: return DateTime.Now;
                //case SqlDbType.DateTime2:return default(DateTime);//?
                //case SqlDbType.DateTimeOffset:return default(DateTimeOffset);//?
                case SqlDbType.Decimal: return default(decimal);
                case SqlDbType.Float: return default(double);
                case SqlDbType.Image: return default(byte[]);//!
                case SqlDbType.Int: return default(Int32);
                case SqlDbType.Money: return default(decimal);
                //case SqlDbType.NChar: return default(string);//!
                case SqlDbType.NChar: return string.Empty;
                //case SqlDbType.NText: return default(string);//!
                case SqlDbType.NText: return string.Empty;
                //case SqlDbType.NVarChar: return default(string);//!
                case SqlDbType.NVarChar: return string.Empty;
                case SqlDbType.Real: return default(Single);
                //case SqlDbType.SmallDateTime: return default(DateTime);//!
                case SqlDbType.SmallDateTime: return DateTime.Now;
                case SqlDbType.SmallInt: return default(Int16);
                case SqlDbType.SmallMoney: return default(decimal);
                //case SqlDbType.Structured:return default(DataTable);//?
                //case SqlDbType.Text: return default(string);//!
                case SqlDbType.Text: return string.Empty;
                //case SqlDbType.Time:return default(DateTime);//?
                //case SqlDbType.Timestamp: return default(byte[]);//!
                case SqlDbType.Timestamp: { byte[] c = new byte[8]; ValueGenerator.NextBytes(c); return c; }
                case SqlDbType.TinyInt: return default(byte);
                //case SqlDbType.Udt:return default(object);//?
                //case SqlDbType.UniqueIdentifier: return default(Guid);//!
                case SqlDbType.UniqueIdentifier: return Guid.NewGuid();
                //case SqlDbType.VarBinary: return default(byte[]);//!
                case SqlDbType.VarBinary: { byte[] c = new byte[1]; ValueGenerator.NextBytes(c); return c; }
                //case SqlDbType.VarChar: return default(string);//!
                case SqlDbType.VarChar: return string.Empty;
                //case SqlDbType.Variant: return default(object);//!
                case SqlDbType.Variant: return new object();
                //case SqlDbType.Xml:return default(System.Data.SqlTypes.SqlXml);//?
                default: return null;
            }
        }

        /// <summary>
        /// 将给定的值转换为与给定的SQL数据类型相兼容的.NET类型
        /// </summary>
        /// <param name="Value">需要转换的值</param>
        /// <param name="ConvertionType">要兼容的目标SQL类型，由SqlDbType枚举表示</param>
        /// <param name="Nullable">指示是否可空，不可空且输入空值时将返回类型默认值</param>
        /// <returns>经过转换后的实例</returns>
        public static object ChangeType(object Value, SqlDbType ConvertionType, bool Nullable = true)
        {
            if (Value == null || Value == DBNull.Value) { if (Nullable) return null; else return GetDefaultValue(ConvertionType); }
            else
                return Convert.ChangeType(Value, GetRuntimeType(ConvertionType));
        }
        /// <summary>
        /// 将给定的值转换为与给定的SQL数据类型相兼容的.NET类型
        /// </summary>
        /// <param name="Value">需要转换的值</param>
        /// <param name="ConvertionTypeString">要兼容的目标SQL类型的SqlDbType枚举名</param>
        /// <param name="Nullable">指示是否可空，不可空且输入空值时将返回类型默认值</param>
        /// <returns>经过转换后的实例</returns>
        public static object ChangeType(object Value, string ConvertionTypeString, bool Nullable = true)
        {
            SqlDbType ConvertionType = (SqlDbType)Enum.Parse(typeof(SqlDbType), ConvertionTypeString, true);
            return ChangeType(Value, ConvertionType, Nullable);
        }
    }
}
